/*
    Dominate 3D - A cube-based game of life

    Copyright (C) 2007, Craig H. Miller (kidmosey@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "option.h"
#include "util.h"

#include "../resource/resource.h"

#ifdef _WIN32_IE
#undef _WIN32_IE
#endif

#define _WIN32_IE 0x0400
#include <windows.h>
#include <commctrl.h>

#include <map>
#include <vector>

typedef std::map<std::string, std::string> OptionMap;

#define D std::pair<std::string, std::string>
D DEFAULT_OPTIONS[] = {
    D("PlayerName",       "Player"),
    D("CubeDimension",    "12"),
    D("NumPlayers",       "3"),
    D("NumBots",          "3"),
    D("Score_TotalCells", "true"),
    D("Score_TakenCells", "true"),
    D("Score_UseTimer",   "false"),
    D("Score_TimeLimit",  "10"),
    D("Score_TimerBased", "true"),
    D("IsServer",         "true"),
    D("ServerPort",       "14440"),
    D("ServerAddress",    "192.168.1.101")
};
#undef D
OptionMap g_Options(DEFAULT_OPTIONS, DEFAULT_OPTIONS+COUNT(DEFAULT_OPTIONS));

OptionMap GetDefaultOptions()
{
    return OptionMap(DEFAULT_OPTIONS, DEFAULT_OPTIONS+COUNT(DEFAULT_OPTIONS));
}

const std::string &GetOption(const std::string &opt)
{
    OptionMap::iterator iter = g_Options.find(opt);
    assert(iter != g_Options.end());

    return (*iter).second;
}

void SetOption(const std::string &opt, const std::string &val)
{
    printf("'%s' = '%s'\n", opt.c_str(), val.c_str());
    g_Options[opt] = val;
}

bool OptionIsEnabled(const std::string &opt)
{
    std::string str = GetOption(opt);

    return stricmp(str.c_str(), "TRUE") == 0 ||
           stricmp(str.c_str(), "YES" ) == 0;
}

void DisableIfNotChecked(HWND hDlg, DWORD cb_id, DWORD ids_to_disable[], size_t count)
{
    DWORD state = FALSE;

    if (SendDlgItemMessage(hDlg, cb_id, BM_GETCHECK, 0, 0) == BST_CHECKED)
        state = TRUE;

    for (size_t i = 0; i < count; i++)
        EnableWindow(GetDlgItem(hDlg, ids_to_disable[i]), state);
}

void InitOptionDlg(HWND hWnd)
{
    // Add the player's name to the edit box.
    SendDlgItemMessage(hWnd, IDC_EDIT3, WM_SETTEXT, 0, (LPARAM)GetOption("PlayerName").c_str());

    // Initialize cells per face spinner
    SendDlgItemMessage(hWnd, IDC_SPIN1, UDM_SETRANGE, 0, MAKELONG(24, 6));
    SendDlgItemMessage(hWnd, IDC_SPIN1, UDM_SETPOS, 0, MAKELONG(atoi(GetOption("CubeDimension").c_str()), 0));

    // jump in increments of 16
    //UDACCEL accel = { 0, 16 };
    //SendDlgItemMessage(hWnd, IDC_SPIN1, UDM_SETACCEL, 1, (LPARAM)&accel);

    // Initialize number of players spinner
#ifdef NDEBUG
    SendDlgItemMessage(hWnd, IDC_SPIN2, UDM_SETRANGE, 0, MAKELONG(24, 2));
#else
    SendDlgItemMessage(hWnd, IDC_SPIN2, UDM_SETRANGE, 0, MAKELONG(24, 1));
#endif
    SendDlgItemMessage(hWnd, IDC_SPIN2, UDM_SETPOS, 0, MAKELONG(atoi(GetOption("NumPlayers").c_str()), 0));

    // Initialize the number of bots spinner
    SendDlgItemMessage(hWnd, IDC_SPIN3, UDM_SETRANGE, 0, MAKELONG(24, 0));
    SendDlgItemMessage(hWnd, IDC_SPIN3, UDM_SETPOS,   0, MAKELONG(atoi(GetOption("NumBots").c_str()), 0));

    // Add the time limit
    SendDlgItemMessage(hWnd, IDC_EDIT4, WM_SETTEXT, 0, (LPARAM)GetOption("Score_TimeLimit").c_str());

#define SET_CHECK_IF_TRUE(opt, id) \
    if (OptionIsEnabled(opt))   \
        SendDlgItemMessage(hWnd, id, BM_SETCHECK, BST_CHECKED, 0);

    SET_CHECK_IF_TRUE("Score_TotalCells", IDC_CHECKBOX1)
    SET_CHECK_IF_TRUE("Score_TakenCells", IDC_CHECKBOX2)
    SET_CHECK_IF_TRUE("Score_UseTimer"  , IDC_CHECKBOX3)
    SET_CHECK_IF_TRUE("Score_TimerBased", IDC_CHECKBOX4)

#undef SET_CHECK_IF_TRUE

    DWORD id[] = { IDC_CHECKBOX4, IDC_EDIT4 };
    DisableIfNotChecked(hWnd, IDC_CHECKBOX3, id, 2);
}

std::string GetDialogItemString(HWND hDlg, DWORD id)
{
    char str[128];

    int len = SendDlgItemMessage(hDlg, id, WM_GETTEXT, sizeof(str), (LPARAM)str);
    str[len] = '\0';

    return std::string(str);
}

OptionMap GetOptionsFromDlg(HWND hWnd)
{
    OptionMap result;

    result["PlayerName"     ] = GetDialogItemString(hWnd, IDC_EDIT3);
    result["CubeDimension"  ] = GetDialogItemString(hWnd, IDC_EDIT1);
    result["NumPlayers"     ] = GetDialogItemString(hWnd, IDC_EDIT2);
    result["NumBots"        ] = GetDialogItemString(hWnd, IDC_EDIT5);
    result["Score_TimeLimit"] = GetDialogItemString(hWnd, IDC_EDIT4);

#define GET_CHECKSTATE(opt, id) \
    if (SendDlgItemMessage(hWnd, id, BM_GETCHECK, 0, 0) == BST_CHECKED) \
        result[opt] = "true"; \
    else \
        result[opt] = "false";

    GET_CHECKSTATE("Score_TotalCells", IDC_CHECKBOX1)
    GET_CHECKSTATE("Score_TakenCells", IDC_CHECKBOX2)
    GET_CHECKSTATE("Score_UseTimer"  , IDC_CHECKBOX3)
    GET_CHECKSTATE("Score_TimerBased", IDC_CHECKBOX4)

#undef GET_CHECKSTATE

    return result;
}

class PrintOption
{
public:
    PrintOption(FILE *fp): m_fp(fp) { }
    void operator() (const OptionMap::value_type &val)
    {
        std::string text = val.first + '=' + val.second + '\n';
        fwrite(text.c_str(), text.length(), 1, m_fp);
    }
    FILE *m_fp;
};

void WriteOptionsToFile(const OptionMap &options, const char fname[])
{
    // write options out to a file
    FILE *fp = fopen(fname, "wt");
    if (fp)
    {
        std::for_each(options.begin(), options.end(), PrintOption(fp));
    }
}

#include <stdio.h>
void ReadOptionsFromFile(const char fname[])
{
    // first, setup the default options
    g_Options = GetDefaultOptions();

    // then, read them in from the file
    std::vector<std::string> opts;
    SplitString(opts, GetFileContents(fname), std::string(" =\n"));

    for each(str, opts.begin(), opts.end())
    {
        std::string key = *str;
        if (++str != opts.end())
        {
            SetOption(key, *str);
        }
    }
}

INT_PTR CALLBACK OptionDlgProc(
    HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    switch (uMsg)
    {
    case WM_INITDIALOG:
        InitOptionDlg(hwndDlg);
    break;
    case WM_COMMAND:
        switch (wParam)
        {
        case IDC_CHECKBOX3:
            switch (HIWORD(wParam))
            {
                case BN_CLICKED:
                {
                    DWORD id[] = { IDC_CHECKBOX4, IDC_EDIT4 };
                    DisableIfNotChecked(hwndDlg, IDC_CHECKBOX3, id, 2);
                }
                break;
            }
            return FALSE;
        case IDOK:
        {
            OptionMap tmp = GetOptionsFromDlg(hwndDlg);
            tmp.insert(g_Options.begin(), g_Options.end());
            g_Options = tmp;

            WriteOptionsToFile(g_Options, "settings.txt");

            EndDialog(hwndDlg, 1);
            return TRUE;
        }

        case IDCANCEL:
            EndDialog(hwndDlg, IDD_MAIN);
            return TRUE;
        }
    }

    return FALSE;
}
